# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##--------------------------------------------------------------------------##
__docformat__ = 'restructuredtext en'
__all__ = []

from ... import comparecards as cmp_cards
from ... import formlib as layout
from ... import pilelib as stack
from ...gamelib import info as gi, game, state
from ...packlib import info as cs
from ...piles import hanafuda as hstack


GT, SL = gi.GT, gi.SL
Wrap = stack.StackWrapper


class Gaji(cs.Hanafuda_Deck, game.Game):
    """
    Finish 1 deck on foundations.

    Layout
      - row [8 horz N , vert offset]
      - talon [N, no offset]
      - foundation [2 horz NW 2 horz NE, no offset]

    :CVariables:
      _talon_ : pysollib.stack.Stack
        .. stack.InitialDealTalonStack

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.VarySuit_Foundation
        Init a base card, not Gaji. Build up in suit by same rank wrap.
        Play top except base.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.GW_FlowerRanks_RowStack (max_cards=12,
          max_accept=12, base_rank=FIRST, strict=False)
        Init 6, 6..4..6, 6 open card. Build down in rank by same suit with
        trash card swap and Gagji is wild. Play any. Base 1st rank.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.defaultHint

    :todo: needs seperate layout
    """
    __info__ = dict(id=0x92b, name='Gaji', category=cs.TYPE_HANAFUDA,
                    game_type=(GT.ONE_DECK | GT.OPEN), decks=1,
                    version='3.20', skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation_ = hstack.VarySuit_Foundation
    _row_ = Wrap(hstack.GW_RankSS_RowStack, max_cards=12, max_accept=12,
                 base_rank=cs.FIRST, strict=False)

    def _create_game(self, rows=8, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (3 * l.XM + (rows + l.NRANK) * l.XS, l.YM + 6 * l.YS,)
        x, y = l.XM, l.YM
        for rank in l.RANK:
            s['foundations'].append(self._foundation_(x, y, game=self,
                                                   rank=rank))
            x += l.XS
            if rank == 1:
                x = self.width - l.XS * 2
        x = 2 * l.XM + 2 * l.XS
        for x_ in l.rows_iter(x, rows):
            s['rows'].append(self._row_(x_, y, game=self))
        self._set_region(s['rows'], l._region(x=x - l.XM, w=x_ + l.XM))
        s['talon'] = self._talon_(self.width - l.XS, self.height - l.YS,
                               game=self)
        l.defaultStackGroups()
        return l

    def _pack_position(self, cards):
        """
        _pack_position(cards) -> tuple

        Place 1 card of each suit of any rank on top, but not Gaji.
        """
        topcards = [None] * 4
        for c in cards[:]:
            if not topcards[c['rank']]:
                if not cmp_cards.isGaji(c):
                    topcards[c['rank']] = c
                    cards.remove(c)
        return cards + topcards

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - TODO

        .. \_start([]) -> None
        """
        for card in self.s['talon'].cards[-4:]:  # Deal foundations save suit
            pile = self.s['foundations'][card['rank']]
            self.s['talon'].dealRow([self.s['foundations'][card['rank']]],
                                    frames=0)
            self._save_stack_caps(pile, base_suit=card['suit'])
        for i in range(4):
            self.s['talon'].dealRow(frames=0)
        for i in range(3, 0, -1):
            self.s['talon'].dealRow(
                                  rows=self.s['rows'][:i] + self.s['rows'][-i:])


class Pagoda(cs.Hanafuda_Deck, game.Game):
    """
    Finish 1 deck on foundations.

    Layout
      - reserve [20 pagoda shape , no offset]
      - talon [SW, no offset]
      - waste [W talon, no offset]
      - foundation [12 over 3 col E, no offset]

    :CVariables:
      _talon_ : pysollib.stack.Stack
        .. stack.WasteTalon (num_deal=4)

        Deal 4 card per `s.waste` on click. No redeal.

      _waste_ : pysollib.stack.Stack
        .. stack.WasteStack (num_deal=4)

          Init 4 card. Build none. Play top.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.Pagoda_Foundation (base_rank=FOURTH,
          max_cards=8)

        Build down in rank by same suit to 1st rank, match, and return. Play
        top.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stack.ReserveStack

        Init 1 card. Build any. Play top. Max 1 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.defaultHint

    :todo: needs seperate layout
    """
    __info__ = dict(id=0x303d, name='Pagoda', category=cs.TYPE_HANAFUDA,
                    game_type=(GT.TWO_DECK | GT.OPEN), decks=2,
                    skill_level=SL.BALANCED, version='3.20')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, num_deal=4)
    _waste_ = stack.WasteStack
    _foundation_ = Wrap(hstack.Pagoda_Foundation, base_rank=cs.FOURTH,
                        max_cards=8)
    _reserve_ = stack.ReserveStack

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (2 * l.XM + 10 * l.XS, 4 * l.YM + 6 * l.YS,)
        align = lambda p: l.XM + (7 - p) / 2 * l.XS
        # Place piles to form a pagado shape for `s.reserves`
        offsets = (1, 2, 1, 0, 1, 2, 1,)
        ddy = l.YM
        for offset in (offsets[1:-1], offsets[2:-2], offsets, (0,) * 5):
            ncols = len(offset)
            for dx, dy in zip(range(ncols), offset):
                x, y = l.delta_coords(x=align(ncols), y=ddy, dx=dx, dy=dy,
                                      fy=4)
                s['reserves'].append(self._reserve_(x, y, game=self))
            ddy += l.YM + l.YS
        # Place piles E with text N to form `s.foundations`
        x, y = self.width - 3 * l.XS, l.YM + l.TEXTHEIGHT
        for suit, (x_, y_) in zip(l.SUIT, l.col_block_iter(x, y,
                                                     npiles=l.SUIT, ncols=3)):
            dy_ = y + suit * l.TEXTHEIGHT
            s['foundations'].append(self._foundation_(x, dy_, game=self,
                                                   suit=suit))
            l.set_misc_text(s['foundations'][-1], anchor='N')
        # Place piles S foundtion to form `s.talon` and `s.waste`
        x -= l.HXS
        y += l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'se')
        x -= l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'sw')
        l.defaultStackGroups()

    @state.not_state('is_preview')
    def _update_text(self):
        """
        _update_text() -> None

        Set `s.foundations` `text.misc` to its state: rising, setting or
        `cap['base_suit']` string value according `SUITS`.

        TODO: Move to pile method
        """
        for s in self.s['foundations']:
            if not s.cards or len(s.cards) == 8:
                text = self.SUITS[s.cap['base_suit']]
            elif len(s.cards) < 5:
                text = _('Rising')
            else:
                text = _('Setting')
            s.texts['misc'].config(text=text)

    def _start(self, reverse=True, **kw):
        """
        Deal initial cards for new game.
          - s[reserves] 1 ea. reversed.

        .. \_start([reverse=True]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['reserves'], reverse=reverse)


class MatsuKiri(cs.Hanafuda_Deck, game.Game):
    """
    Drop 1 deck by full seq on foundations.

    Layout
      - row [8 horz N, vert offset]
      - talon [SE, no offset]
      - foundation [1 NE, vert offset]

    :CVariables:
      _talon_ : pysollib.stack.Stack
        .. stack.InitialDealTalon

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.MatsuKiri_Foundation (strict=False)

        Build none. Drop full _row_ sequence in suit order. Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.DropRankSS_RowStack (max_cards=12,
          max_accept=12, base_rank=FIRST, strict=False)

        Init 6 card. Build down in rank by same suit with trash card swap
        except Willow. Play any. Max 12 card. Base 1st rank. Drop full seq on
        foundation in suit order.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.defaultHint

    :todo: needs seperate layout
    """
    __info__ = dict(id=0x49d0a, name='MatsuKiri', category=cs.TYPE_HANAFUDA,
                    game_type=(GT.SPIDER | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='3.20')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation_ = Wrap(hstack.MatsuKiri_Foundation, strict=False)
    _row_ = Wrap(hstack.DropRankSS_RowStack, max_cards=12, max_accept=12,
                 base_rank=cs.FIRST, strict=False)

    def _create_game(self, rows=8, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (3 * l.XM + (rows + 1) * l.XS, l.YM + l.YS * 6,)
        y = l.YM
        for x in l.row_iter(l.XM, npiles=rows):
            s['rows'].append(self._row_(x, y, game=self, offset=(0, l.CH / 2)))
        self._set_region(s['rows'], l._region(w=x + l.XS + l.XM))
        x += l.XS + l.XM * 2
        s['foundations'].append(self._foundation_(x, y, game=self,
                                               offset=(0, l.YOFFSET)))
        self._set_region(s['foundations'], l._region(x=x - l.XM))
        s['talon'] = self._talon_(self.width - l.XS, self.height - l.YS,
                               game=self)
        l.defaultStackGroups()

    def _start(self, rows=6, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 6 ea up.

        .. \_start([rows=6]) -> None
        """
        for i in range(rows - 1):
            self.s['talon'].dealRow(frames=0)
        self.s['talon'].dealRow()


class MatsuKiriStrict(MatsuKiri):
    """
    Drop 1 deck by full seq on foundations.

    Layout
      - row [8 horz N, vert offset]
      - talon [SE, no offset]
      - foundation [1 NE, vert offset]

    :CVariables:
      _talon_ : pysollib.stack.Stack
        .. stack.InitialDealTalon

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.MatsuKiri_Foundation

        Build none. Drop full _row_ sequence in suit order. Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.DropRankSS_RowStack (max_cards=12,
          max_accept=12, base_rank=FIRST)

        Init 6 card. Build down in rank by same suit. Play any. Max 12
        card. Base 1st rank. Drop full seq on foundation in suit order.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.defaultHint
    """
    __info__ = dict(id=0x49d24, name='MatsuKiri Strict',
                    category=cs.TYPE_HANAFUDA, game_type=(GT.SPIDER |
                    GT.OPEN), decks=1, skill_level=SL.MOSTLY_SKILL,
                    version='0.50fc')
    __author__ = 'Mt. Hood Playing Card'
    __version__ = 7.70

    _foundation_ = hstack.MatsuKiri_Foundation
    _row_ = Wrap(hstack.DropRankSS_RowStack, max_cards=12, max_accept=12,
                 base_rank=cs.FIRST)


class GreatWall(cs.Hanafuda_Deck, game.Game):
    """
    Drop 1 deck by full seq on foundations.

    Layout
      - row [12 horz N, vert offset]
      - talon [S, no offset]
      - foundation [2 vert E, 2 vert W, vert offset]

    :CVariables:
      _talon_ : pysollib.stack.Stack
        .. stack.InitialDealTalon

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.Suit_Foundation (max_cards=48,
          max_move=1, min_accept=1, max_accept=1, mod=12)

        Build up in suit by same rank wrap. Play top except base. Max 48 card
        (4 suit sets).

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.GreatWall_RowStack (max_cards=26,
          max_accept=26, base_rank=FIRST, mod=4, strict=False)

        Init 6 card. Build down in rank alone with trash cards swap
        except for Willow or up in suit alone both wrap. Play any. Max 26
        card. Base 1st rank.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.defaultHint

    :todo: needs seperate layout
    """
    __info__ = dict(id=0x7e5f, name='Great Wall', category=cs.TYPE_HANAFUDA,
                    game_type=(GT.FOUR_DECK | GT.OPEN), decks=4,
                    skill_level=SL.MOSTLY_SKILL, version='3.20')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation_ = Wrap(hstack.Suit_Foundation, max_cards=48,
                        max_move=1, min_accept=1, max_accept=1, mod=12)
    _row_ = Wrap(hstack.GreatWall_RowStack, max_cards=26, max_accept=26,
                 base_rank=cs.FIRST, mod=4, strict=False)

    def _create_game(self, rows=12, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (3 * l.XM + (rows + 2) * l.XS, l.YM + l.YS * 6,)
        delta = zip((l.XM,) * 2 + (self.width - l.XS,) * 2,
                    (l.YM, self.height / 2,) * 2)
        for (x, y), rank in zip(delta, l.RANK):
            s['foundations'].append(self._foundation_(x, y, game=self, rank=rank,
                                                   offset=(0, l.CH / 2)))
            l.set_misc_text(s['foundations'][-1], anchor='n')
        x = 2 * l.XM + l.XS
        y = l.YM
        for x_ in l.row_iter(x, npiles=rows):
            s['rows'].append(self._row_(x_, y, game=self, offset=(0, l.CH / 4)))
        self._set_region(s['rows'], l._region(x=x, w=x_ + l.XS))
        s['talon'] = self._talon_((self.width + l.XS) / 2,
                                self.height - l.YS, game=self)
        l.defaultStackGroups()

    @state.not_state('is_preview')
    def _update_text(self):
        """
        _update_text() -> None

        Set `s.foundations` `text.misc` to its state: 1st, 2nd, 3rd, 4th,
        Filled for each full suit set.

        TODO: Move to pile method
        """
        for stack in self.s['foundations']:
            l = len(stack.cards) / 12
            if l == 0:
                text = ''
            elif l == 4:
                text = _('Filled')
            else:
                text = _('{}{} deck').format(l, (_('st'), _('nd'), _('rd'),
                                                 _('th'))[l - 1])
            stack.texts['misc'].config(text=text)

    def _start(self, rows=16, flip=False, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 16 ea top.

        .. \_start([rows=16, flip=False]) -> None
        """
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self.s['talon'].dealRow()


class Wisteria(cs.Hanafuda_Deck, game.Game):
    """
    Finish 1 deck by full seq on foundations.

    Layout
      - row [13 horz S foundation, vert offset]
      - talon [SE, no offset]
      - foundation [12 over 6 cols N, vert offset]

    :CVariables:
      _talon_ : pysollib.stack.Stack
        .. stack.InitialDealTalon

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.Rank_Foundation

        Build up in rank by same suit. Play Top. Max 48 card (4 suit sets).

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.hanafuda.RankSS_RowStack (base_rank=cs.GAP)

        Init all card with each 1st rank deals next pile. Build down in
        rank by same suit. Play any. Base none.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.defaultHint
    """
    __info__ = dict(id=0x18ff0, name='Wisteria', category=cs.TYPE_HANAFUDA,
                    game_type=(GT.FREECELL | GT.OPEN), decks=1, redeals=0,
                    skill_level=SL.MOSTLY_SKILL, version='0.50fc')
    __author__ = 'Mt. Hood Playing Card'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation_ = hstack.Rank_Foundation
    _row_ = Wrap(hstack.RankSS_RowStack, base_rank=cs.GAP)

    def _create_game(self, rows=13, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + rows * l.XS, l.YM + 6 * l.YS,)
        x, y = self.width / 2 - l.XS * 3, l.YM
        for (x, y), suit in zip(l.col_block_iter(x, y, l.SUIT, ncols=6),
                                l.SUIT):
            s['foundations'].append(self._foundation_(x, y, self, suit=suit))
        self._set_region(self.s['foundations'], l._region(h=l.YM + l.YS * 2),
                        priority=1)
        x, y = l.XM, l.YM + l.YS * 2
        for x in l.row_iter(x, npiles=rows):
            s['rows'].append(self._row_(x, y, game=self))
        s['talon'] = self._talon_(self.width - l.XS, self.height - l.YS,
                               game=self)
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] all. next before FIRST RANK

        .. \_start([rows=6]) -> None
        """
        i = 0
        while self.s['talon'].cards:
            if self.s['talon'].TOPCARD['rank'] == cs.FIRST and self.s['rows'][i].cards:
                    i += 1
            self.s['talon'].dealRow(rows=[self.s['rows'][i]], frames=4)


PROVIDES = [Gaji, GreatWall, MatsuKiri, MatsuKiriStrict, Pagoda, Wisteria]